#pragma once

#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

#define SIZE 4096
#define MODE_DEFAULT 0666
#define EXIT_ERROR(m) \
    do                \
    {                 \
        perror(m);    \
        exit(1);      \
    } while (0)

#define PATH_NAME "."
#define PROJ_ID 1

class Shm
{
private:
    void Create(const key_t key, const int shmflg)
    {
        _shmid = shmget(key, SIZE, shmflg);
        if (_shmid < 0)
        {
            EXIT_ERROR("shemget");
        }
        printf("create shm success!\n");
    }

public:
    Shm(const std::string &pathname, const int projid, const std::string &identity)
        : _identity(identity)
    {
        // 1.创建共享内存
        key_t key = ftok(pathname.c_str(), projid);
        if (_identity == "user")
        {
            Create(key, IPC_CREAT);
        }
        else if (_identity == "server")
        {
            Create(key, IPC_CREAT | IPC_EXCL | MODE_DEFAULT);
        }
        else
        {
            printf("identity false!\n");
            exit(1);
        }
    }

    ~Shm()
    {
        // 4.删除共享内存
        if (_identity == "server")
        {
            int ret = shmctl(_shmid, IPC_RMID, nullptr);
            if (ret < 0)
            {
                EXIT_ERROR("shmctl");
            }
            printf("delete shm success!\n");
        }
    }

    void Attach()
    {
        // 2.挂载，将共享内存映射到进程地址空间中，拿到起始虚拟地址
        _addr = shmat(_shmid, nullptr, 0);
        if ((long long)_addr < 0)
        {
            EXIT_ERROR("shmat");
        }
        printf("attach shm success!\n");
    }

    void *Virtualaddr() const { return _addr; }

    void Detach() const
    {
        // 3.去关联
        int ret = shmdt(_addr);
        if (ret < 0)
        {
            EXIT_ERROR("shmdt");
        }
        printf("detach shm success!\n");
    }

private:
    int _shmid;
    void *_addr;
    std::string _identity;
};